/*
 * This file is part of quickReminder, a software for gamers
 *
 * Copyright (C) 2006-2008 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Xml.Serialization;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Net;
using System.IO;
using ICSharpCode.SharpZipLib;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;
using HtmlAgilityPack;
using ESLReminder;
using System.Threading;

namespace LucasCode
{
    public class ParseException : Exception
    {
        public ParseException(string error, string html)
            : base(error)
        {
            HTMLSource = html;
        }
        public string HTMLSource = string.Empty;
        //nothing
    };

    public class FileStatusChangedEventArgs : EventArgs
    {
        MatchFile m_file;
        public MatchFile File
        {
          get { return m_file; }
          set { m_file = value; }
        }

        public FileStatusChangedEventArgs(MatchFile file)
        {
            m_file = file;
        }
    };

    [Serializable]
    public class MatchArchive : IStaticDataSource
    {
        private string m_Comment = String.Empty;
        /* lists */
        private List<MatchContestant> m_Contestants = new List<MatchContestant>();
        private List<MatchRound> m_Rounds = new List<MatchRound>();
        private List<MatchFile> m_Files = new List<MatchFile>();
        private List<MatchPlayer> m_TeamlessPlayers = new List<MatchPlayer>();

        private DateTime m_DateCreated = DateTime.Now;
        
        private string m_League;
        private string m_Matchlink;
        private string m_Filename;
        private ZipFile m_ZipFile;
        private static string m_XmlFilename = "details.xml";
        private int m_Progress;
        private DateTime m_MatchDate = DateTime.MinValue;

        public event EventHandler ProgressChanged;
        public event EventHandler<FileStatusChangedEventArgs> FileStatusChanged;
        public event EventHandler UploadComplete;

        public MatchArchive()
        {
            
        }

        public MatchPlayer[] TeamlessPlayers
        {
            get
            {
                return m_TeamlessPlayers.ToArray();
            }
            set
            {
                m_TeamlessPlayers.Clear();
                m_TeamlessPlayers.AddRange(value);
            }
        }

        public void AddTeamlessPlayer(MatchPlayer player)
        {
            m_TeamlessPlayers.Add(player);
        }

        public static MatchArchive[] getOpenMatches(string cookie)
        {
            string html = string.Empty;
            List<MatchArchive> matchlist = new List<MatchArchive>();
            HtmlAgilityPack.HtmlDocument d;
            //get html source
            html = MatchHelpers.GetHTML("http://www.esl.eu/change_lang/en/?ref=/open_matches/", cookie);
            d = new HtmlAgilityPack.HtmlDocument();
            d.LoadHtml(html);


            //datetime + login-status
            HtmlNode time = d.DocumentNode.SelectSingleNode(@"//div[@id=""datetime""]/div[2]");
            if (time == null)
                throw new ParseException("Could not parse website header!", html);
            bool isLoggedIn = time.SelectSingleNode("b/a").Attributes["href"].Value.Trim().EndsWith("/logout/");
            if (!isLoggedIn)
                throw new ParseException("Invalid login!", html);

            HtmlNode matchinfos = d.DocumentNode.SelectSingleNode(@"//div[@id=""main_content""]");
            if (matchinfos == null)
                throw new ParseException("Could not get main content!", html);

            HtmlNodeCollection matches = matchinfos.SelectNodes("div/table/tr[position()>=3]");

            //do we have some matches?
            if (matches != null)
            {
                foreach (HtmlNode n in matches)
                {
                    string datestr = n.SelectSingleNode("td[1]").InnerText.Trim();
                    //strip "Today, " or "Heute, " from string
                    datestr = datestr.Substring(datestr.IndexOf(',') + 1, datestr.Length - datestr.IndexOf(',') - 1);
                    DateTime matchdate = DateTime.Parse(datestr);

                    string enemy = n.SelectSingleNode("td[2]").InnerText.Trim();
                    //strip "( )" from name
                    string team = Regex.Match(n.ParentNode.SelectSingleNode("tr[1]//span").InnerText.Trim(), @"^\( (.*?) \)$").Groups[1].Value;
                    string enemyurl = n.SelectSingleNode("td[2]//a[@href]").Attributes["href"].Value.Trim();
                    string enemyid = MatchHelpers.getIDFromLink(enemyurl);

                    string matchurl = n.SelectSingleNode("td[5]//a[@href]").Attributes["href"].Value.Trim();



                    MatchArchive a = new MatchArchive();
                    //teams
                    MatchContestant[] teams = new MatchContestant[2];
                    teams[0].Name = team;
                    teams[1].Name = enemy;
                    teams[1].ID = enemyid;
                    a.Contestants = teams;
                    a.OwnTeam = teams[0];
                    //matchinfo
                    a.MatchDate = matchdate;
                    //a.League = league;
                    a.Matchlink = matchurl;
                    matchlist.Add(a);
                }
            }
            return matchlist.ToArray();
        }


        public static MatchArchive getArchiveInfo(string filename)
        {
            int XmlDetailsIndex;

            ZipFile m_ZipFile = null;
            try
            {
                m_ZipFile  = new ZipFile(filename);
            }
            catch
            {
                return null;
            }
            if ((XmlDetailsIndex = m_ZipFile.FindEntry(m_XmlFilename, true)) >= 0)
            {
                XmlSerializer xml = new XmlSerializer(typeof(MatchArchive));
                using (Stream s = m_ZipFile.GetInputStream(XmlDetailsIndex))
                {
                    MatchArchive archive = (MatchArchive)xml.Deserialize(s);
                    return archive;
                }
            }
            else
                return null;
        }

        public MatchArchive(string filename) : this(filename, false)
        {
            /* empty */
        }



        public MatchArchive(string filename, bool create_new)
        {
            if (File.Exists(filename) && !create_new)
            {
                int XmlDetailsIndex;
                m_ZipFile = new ZipFile(filename);
                if ((XmlDetailsIndex = m_ZipFile.FindEntry(m_XmlFilename, true)) >= 0)
                {
                    XmlSerializer xml = new XmlSerializer(this.GetType());
                    using (Stream s = m_ZipFile.GetInputStream(XmlDetailsIndex))
                    {
                        MatchArchive archive = (MatchArchive)xml.Deserialize(s);
                        ImportFrom(archive);
                    }
                }
            }
            else
            {
                CreateArchive(filename);
            }
            m_ZipFile.IsStreamOwner = true;
            this.m_Filename = filename;
        }

        public void CreateArchive(string filename)
        {
            if (m_ZipFile != null)
                throw new Exception("You cannot recreate an archive at a different destination!");
            if (!Directory.Exists(Path.GetDirectoryName(filename)))
                Directory.CreateDirectory(Path.GetDirectoryName(filename));
            m_ZipFile = ZipFile.Create(filename);
            m_ZipFile.BeginUpdate();
            m_ZipFile.Add(this, m_XmlFilename, CompressionMethod.Deflated);
            m_ZipFile.CommitUpdate();
            m_ZipFile.IsStreamOwner = true;
        }

        ~MatchArchive()
        {
            Close();
        }

        public Stream GetSource()
        {
            XmlSerializer xml = new XmlSerializer(this.GetType());
            MemoryStream m = new MemoryStream();
            xml.Serialize(m, this);
            m.Seek(0, SeekOrigin.Begin);
            return m;
        }

        private StringCollection m_AequitasIDs = new StringCollection();

        public string[] AequitasIDs
        {
            get
            {
                string[] ids = new string[m_AequitasIDs.Count];
                m_AequitasIDs.CopyTo(ids, 0);
                return ids;
            }
            set
            {
                m_AequitasIDs.Clear();
                m_AequitasIDs.AddRange(value);
            }
        }



        private void ImportFrom(MatchArchive source)
        {
            this.Comment = source.Comment;
            this.Contestants = source.Contestants;
            this.DateCreated = source.DateCreated;
            this.Files = source.Files;
            this.Matchlink = source.Matchlink;
            this.Rounds = source.Rounds;
            this.Filename = source.Filename;
            this.MatchDate = source.MatchDate;
            this.Gamename = source.Gamename;
            this.Modname = source.Modname;
        }

        public string Comment
        {
            get
            {
                return m_Comment;
            }
            set
            {
                m_Comment = value;
            }
        }
        [XmlIgnore]
        public MatchPlayer[] Players
        {
            get
            {
                int PlayerCount = 0, i = 0;
                foreach (MatchContestant team in m_Contestants)
                    PlayerCount += team.PlayerCount;
                PlayerCount += m_TeamlessPlayers.Count;
                MatchPlayer[] players = new MatchPlayer[PlayerCount];
                foreach (MatchContestant team in m_Contestants)
                {
                    team.Players.CopyTo(players, i);
                    i += team.PlayerCount;
                }
                TeamlessPlayers.CopyTo(players, i);
                return players;
            }
            set
            {
            }
        }

        public DateTime DateCreated
        {
            get
            {
                return m_DateCreated;
            }
            set
            {
                m_DateCreated = value;
            }
        }

        public MatchFile[] Files
        {
            get
            {
                return m_Files.ToArray();
            }
            set
            {
                m_Files.Clear();
                foreach (MatchFile f in value)
                {
                    FileAdd(f);
                }
            }
        }

        [XmlIgnore]
        public string League
        {
            get
            {
                if (string.IsNullOrEmpty(m_Matchlink))
                    return String.Empty;
                Match m = Regex.Match(m_Matchlink, @"^(.*?)\d+/$");
                if (m.Success)
                    return m.Groups[1].Value;
                return string.Empty;
            }
        }

        public string Matchlink
        {
            get
            {
                return m_Matchlink;
            }
            set
            {
                m_Matchlink = value;
            }
        }

        [XmlIgnore]
        public string Filename
        {
            get
            {
                return m_Filename;
            }
            set
            {
            }
        }

        public MatchContestant[] Contestants
        {
            get
            {
                return m_Contestants.ToArray();
            }
            set
            {
                m_Contestants.Clear();
                m_Contestants.AddRange(value);
            }
        }

        private Bitmap GetBitmapFromURL(string URL)
        {
            Stream strm = null;
            Bitmap b = null;
            HttpWebRequest request;
            HttpWebResponse response = null;
            StreamReader answer = null;
            request = (HttpWebRequest)WebRequest.Create(URL);
            request.Timeout = 30000;
            //System.Net.WebClient Client = new WebClient();
            try
            {
                response = (HttpWebResponse)request.GetResponse();
                strm = response.GetResponseStream();
                //strm = Client.OpenRead(URL);
                b = new Bitmap(strm);
            }
            catch(Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (response != null)
                    response.Close();
                if (strm != null)
                    strm.Close();
            }
            return b;
        }

        public void CompleteUpdate(string cookie)
        {
            double i = 0;
            UpdateProgress(0);
            int max = m_TeamlessPlayers.Count;
            if (string.IsNullOrEmpty(m_Matchlink))
            {
                foreach (MatchPlayer p in m_TeamlessPlayers)
                {
                    try
                    {
                        p.lookupPlayer();
                    }
                    catch{}
                    UpdateProgress((++i / max) * 100);
                }
            }
            else
            {
                //max = m_Contestants.Count * m_TeamlessPlayers.Count;
                //leaguemode
                try
                {
                    UpdateDetailsFromWeb(cookie, MatchInfoFlags.AequitasInfo |
                MatchInfoFlags.MapInfo |
                MatchInfoFlags.PlayerInfo |
                MatchInfoFlags.ScoreInfo |
                MatchInfoFlags.TeamInfo);
                }
                catch{}
                List<MatchPlayer> assigned = new List<MatchPlayer>();
                foreach (MatchContestant c in m_Contestants)
                {
                    MatchPlayer[] players = new MatchPlayer[0];
                    try
                    {
                        players = c.getMemberList();
                    }
                    catch { }
                    foreach (MatchPlayer tp in m_TeamlessPlayers)
                    {
                        foreach (MatchPlayer p in players)
                        {
                            if (p.GameAccountList.Contains(tp.GameID))
                            {
                                //found player
                                tp.ESLNick = p.ESLNick;
                                tp.GameAccounts = p.GameAccounts;
                                tp.IsBarred = p.IsBarred;
                                tp.UserID = p.UserID;
                                if (string.IsNullOrEmpty(tp.CurrentNick))
                                    tp.CurrentNick = p.CurrentNick;
                                tp.IsArchiveOwner = p.IsArchiveOwner;
                                tp.HasAequitasLog = hasAequitasLog(tp);

                                c.AddPlayer(tp);
                                assigned.Add(tp);
                                UpdateProgress((++i / max) * 100);
                            }
                        }
                    }
                }
                foreach(MatchPlayer p in assigned)
                    m_TeamlessPlayers.Remove(p);
                foreach (MatchPlayer p in m_TeamlessPlayers)
                {
                    try
                    {
                        p.lookupPlayer();
                    }
                    catch { }
                    p.HasAequitasLog = hasAequitasLog(p);
                    UpdateProgress((++i / max) * 100);
                }
            }
        }

        private bool hasAequitasLog(MatchPlayer player)
        {
            if (m_AequitasIDs.Contains(player.UserID))
            {
                return true;
            }
            return false;
        }

        public string GetReplacedString(string s)
        {
            s = s.Replace(@"%enemy%", GetReplacement(Enemy != null ? Enemy.Name : "#unknown#"));
            s = s.Replace(@"%ownteam%", GetReplacement(OwnTeam != null ? OwnTeam.Name : "#unknown#"));
            s = s.Replace(@"%league%", GetReplacement(m_League));
            s = s.Replace(@"%matchdate%", GetReplacement(MatchDate.ToString("yy-MM-dd")));
            s = s.Replace(@"%maps%", GetReplacement(GetMaps()));
            
            return s;
        }

        private string GetMaps()
        {
            StringBuilder b = new StringBuilder();
            foreach (MatchRound r in m_Rounds)
            {
                b.Append(r.Map);
                b.Append(" & ");
            }
            //remove " & " at the end
            if (b.Length >= 3)
                b.Remove(b.Length - 3, 3);
            return b.ToString();
        }

        private string GetReplacement(string s)
        {
            if (string.IsNullOrEmpty(s))
                return "#unknown#";
            else
            {
                char[] invalid1 = Path.GetInvalidPathChars();
                char[] invalid2 = Path.GetInvalidFileNameChars();

                foreach (char c in invalid1)
                {
                    s = s.Replace(c, '_');
                }
                foreach (char c in invalid2)
                {
                    s = s.Replace(c, '_');
                }
                return s;
            }
        }

        public void UpdateDetailsFromWeb(string cookie, MatchInfoFlags flags)
        {
            if (string.IsNullOrEmpty(m_Matchlink) || flags == MatchInfoFlags.None)
                return;
            UpdateProgress(10);
            string HTML = string.Empty;
            HTML = MatchHelpers.GetHTML(m_Matchlink, cookie);
            UpdateProgress(50);
            //string orightml = HTML;
            //Match PreFilter = Regex.Match(HTML, @"<div id=""main_content"">.*?<div id=""footer"">", RegexOptions.Singleline);
            //if (PreFilter.Success)
                //HTML = PreFilter.Value;

            HtmlAgilityPack.HtmlDocument d = new HtmlAgilityPack.HtmlDocument();
            d.LoadHtml(HTML);
            HtmlNode matchinfos = d.DocumentNode.SelectSingleNode(@"/html/body/div/div/div[@id=""page""]/div[@id=""main""]/div[@id=""col3""]/div[@id=""col3_content""]/div[@id=""main_content""]/table");
            HtmlNodeCollection Points = matchinfos.SelectNodes("tr[5]/td/table/tr[position()<3]/td/b");
            HtmlNodeCollection Maps = matchinfos.SelectNodes("tr[5]/td[1]/table[1]/tr[position()<3]/td[position() = 1 and text() != ''] | tr[3]/td[2]/table/tr[5]/td[2]");

            HtmlNodeCollection Contestants = matchinfos.SelectNodes("tr[3]/td/table/tr[2]/td/a[1]");
            HtmlNodeCollection Aequitas = matchinfos.SelectNodes("tr[7]/td/table/tr");
            HtmlNode Ladder = d.DocumentNode.SelectSingleNode(@"//div[@id=""navnode_navigation""]/div[@id=""qm0""]/a[last()]");
            if (Ladder != null)
                m_League = Ladder.InnerText.Trim();

            UpdateProgress(60);

            if ((flags & MatchInfoFlags.ScoreInfo) > 0)
            {
                m_Rounds.Clear();
                for (int i = 0; i < Points.Count / 2; i++)
                {
                    MatchRound r = new MatchRound();
                    if (Maps != null)
                    {
                        r.Map = Maps[i].InnerText;
                    }
                    try
                    {
                        r.Scores[0] = int.Parse(Points[i * 2].InnerText);
                        r.Scores[1] = int.Parse(Points[(i * 2) + 1].InnerText);
                    }
                    catch { }

                    m_Rounds.Add(r);
                }
            }
            


            /* this is what a teamid list in the ESL source looks like
            ESLteamID.unshift(2470639);
            ESLteamID.unshift(1645201);
            ESLteamID.unshift(2804185);
            ESLteamID.unshift(2658316);
            ESLteamID.unshift(2466101);
            ESLteamID.unshift(2466119);
            ESLteamID.unshift(2659890);
            ESLteamID.unshift(2489288);
            ESLteamID.unshift(1642763); */
            

            

            if ((flags & MatchInfoFlags.TeamInfo) > 0)
            {
                m_Contestants.Clear();
                OwnTeam = null;
                MatchCollection MyTeamIDs = Regex.Matches(HTML, @"\bESLteamID.unshift\((\d+)\);");
                StringCollection TeamIDs = new StringCollection();
                foreach (Match m in MyTeamIDs)
                {
                    TeamIDs.Add(m.Groups[1].Value);
                }
                foreach (HtmlNode n in Contestants)
                {
                    MatchContestant team = new MatchContestant();
                    team.ID = MatchHelpers.getIDFromLink(n.Attributes["href"].Value);

                    team.Logo = GetBitmapFromURL(n.SelectSingleNode("img").Attributes["src"].Value);
                    UpdateProgress(m_Progress + 20);
                    team.Name = n.InnerText;
                    if (n.Attributes["href"].Value.Contains("/player/"))
                    {
                        team.Type = MatchContestantTypes.Individual;
                        MatchPlayer p = new MatchPlayer();
                        p.CurrentNick = team.Name;
                        p.ESLNick = team.Name;
                        p.UserID = team.ID;
                        team.AddPlayer(p);
                    }
                    m_Contestants.Add(team);
                    if (TeamIDs.Contains(team.ID))
                        OwnTeam = team;
                }
            }

            if ((flags & MatchInfoFlags.PlayerInfo) > 0)
            {
                m_AequitasIDs.Clear();
                Owner = null;
                string MyUserID = "none";
                Match MyUserIDRegex = Regex.Match(HTML, @"ESLuserID = (\d+)");
                if (MyUserIDRegex.Success)
                    MyUserID = MyUserIDRegex.Groups[1].Value;
                foreach (HtmlNode n in Aequitas)
                {
                    
                    MatchPlayer player = new MatchPlayer();
                    player.UserID = MatchHelpers.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value);
                    m_AequitasIDs.Add(player.UserID);
                    player.ESLNick = n.SelectSingleNode("td[1]/a").InnerText;
                    player.CurrentNick = player.ESLNick;
                    player.HasAequitasLog = true;
                    string teamid = MatchHelpers.getIDFromLink(n.SelectSingleNode("td[2]/a").Attributes["href"].Value);
                    if (player.UserID == MyUserID)
                        Owner = player;
                    bool foundteam = false;
                    foreach (MatchContestant c in m_Contestants)
                    {
                        if (foundteam)
                            break;
                        switch (c.Type)
                        {
                            case MatchContestantTypes.Individual:
                                if (c.ID == player.UserID)
                                {
                                    foundteam = true;
                                    c.Players[0].HasAequitasLog = true;
                                    //c.AddPlayer(player);
                                    break;
                                }
                                break;
                            case MatchContestantTypes.Team:
                                if (c.ID == teamid)
                                {
                                    foundteam = true;
                                    c.AddPlayer(player);
                                    break;
                                }
                                break;
                        }
                    }
                    if (!foundteam)
                    {
                        bool dupfound = false;
                        foreach (MatchContestant c in m_Contestants)
                        {
                            if (c.searchDuplicate(player) != null)
                            {
                                c.AddPlayer(player);
                                dupfound = true;
                                break;
                            }
                        }
                        if (!dupfound)
                        {
                            foreach (MatchPlayer p in m_TeamlessPlayers)
                                if (p.CompareTo(player) == 0)
                                    dupfound = true;
                            if (!dupfound)
                                m_TeamlessPlayers.Add(player);

                        }
                        
                    }
                }
            }
            WriteArchiveDetails();
            UpdateProgress(100);
        }

        public MatchFile FileInfo(string filename)
        {
            foreach (MatchFile f in m_Files)
            {
                if (Path.GetFileName(filename) == f.Filename)
                {
                    return f;
                }
            }
            throw new System.IO.FileNotFoundException();
        }

        public bool FileIsArchived(string file)
        {
            if (m_ZipFile == null)
                return false;
            int index = m_ZipFile.FindEntry(file, true);
            if (index >= 0)
                return true;
            else
                return false;
        }

        public bool FileExists(string filename)
        {
            bool found = false;
            foreach (MatchFile f in m_Files)
            {
                if (Path.GetFileName(filename) == f.Filename)
                {
                    found = true;
                    break;
                }
            }
            return found;
        }

        public void FilesAdd(System.Collections.Specialized.NameValueCollection files)
        {
            /*for (int i=0; i<files.Count; i++)
            {
                FileAdd(files.GetKey(i), files.Get(i));
                UpdateProgress((i + 1) * 100 / files.Count);
            }*/
        }

        /*public void FileAdd(string filename)
        {
            FileAdd(filename, Path.GetFileName(filename));
        }*/

        public void FileProcess(MatchFile f)
        {
            if (!m_Files.Contains(f))
                throw new FileNotFoundException("This file does not belong to the archive");
            if (f.IsArchived)
                throw new IOException("This file does already exist in the archive");

            FileInfo info = new FileInfo(f.Filename);
            if (!info.Exists)
                throw new System.IO.FileNotFoundException("Inputfile not found!");

            //MatchFile file = new MatchFile(f.TargetName);
            //FileAdd(file);

            f.DateArchived = DateTime.Now;
            f.DateCreated = info.CreationTime;
            f.LastChanged = info.LastWriteTime;
            m_ZipFile.BeginUpdate();
            string path = Path.GetDirectoryName(Path.GetFullPath(f.Filename));
            m_ZipFile.NameTransform = new ZipNameTransform(path);
            m_ZipFile.Add(f, f.TargetName + Path.GetExtension(f.Filename), CompressionMethod.Deflated);
            m_ZipFile.CommitUpdate();
            f.Filename = f.TargetName + Path.GetExtension(f.Filename);
            //if (FileExists(targetname))
                //FileRemove(targetname);
            
            WriteArchiveDetails();
        }

        public void AddFile(MatchFile f)
        {
            FileAdd(f);
        }

        private void FileAdd(MatchFile f)
        {
            f.Parent = this;
            m_Files.Add(f);
        }

        public void Close()
        {
            if (m_ZipFile != null)
            {
                //WriteArchiveDetails();
                m_ZipFile.Close();
            }
        }

        public void WriteArchiveDetails()
        {
            if (m_ZipFile == null)
                return;
            try
            {
                m_ZipFile.BeginUpdate();
                m_ZipFile.Add(this, m_XmlFilename, CompressionMethod.Deflated);
                m_ZipFile.CommitUpdate();
            }
            catch
            {
                m_ZipFile.BeginUpdate();
                m_ZipFile.Add(this, m_XmlFilename, CompressionMethod.Deflated);
                m_ZipFile.CommitUpdate();
            }
        }

        public MatchRound[] Rounds
        {
            get
            {
                return m_Rounds.ToArray();
            }
            set
            {
                m_Rounds.Clear();
                m_Rounds.AddRange(value);
            }
        }

        public void FileRemove(string filename)
        {
            if (!FileExists(filename))
                throw new System.IO.FileNotFoundException();
            m_ZipFile.BeginUpdate();
            try { m_ZipFile.Delete(filename); }
            catch{}
            m_ZipFile.CommitUpdate();
            m_Files.Remove(FileInfo(filename));
            WriteArchiveDetails();
        }

        private void UpdateProgress(double progress)
        {
            UpdateProgress((int)Math.Round(progress));
        }

        private void UpdateProgress(int progress)
        {
            if (progress > 100)
                m_Progress = 100;
            else if (progress < 0)
                m_Progress = 0;
            else
                m_Progress = progress;
            if (ProgressChanged != null)
                ProgressChanged(this, new EventArgs());
        }

        [XmlIgnore]
        public int Progress
        {
            get
            {
                return m_Progress;
            }
        }

        [XmlIgnore]
        public MatchPlayer Owner
        {
            get
            {
                foreach (MatchPlayer p in Players)
                    if (p.IsArchiveOwner)
                        return p;
                return null;
            }
            set
            {
                foreach (MatchPlayer p in Players)
                    p.IsArchiveOwner = false;
                if (value != null)
                    value.IsArchiveOwner = true;
            }
        }

        [XmlIgnore]
        public MatchContestant OwnTeam
        {
            get
            {
                foreach (MatchContestant c in m_Contestants)
                    if (c.IsOwnerTeam)
                        return c;
                return null;
            }
            set
            {
                foreach (MatchContestant c in m_Contestants)
                    c.IsOwnerTeam = false;
                if (value != null)
                    value.IsOwnerTeam = true;
            }
        }

        [XmlIgnore]
        public MatchContestant Enemy
        {
            get
            {
                MatchContestant enemy = null;
                foreach (MatchContestant c in m_Contestants)
                {
                    if (!c.IsOwnerTeam)
                    {
                        if (enemy != null)
                            return null;
                        else
                            enemy = c;
                    }
        
                }
                return enemy;
            }
        }

        [XmlIgnore]
        public MatchContestant Winner
        {
            get
            {
                int Score1 = 0, Score2 = 0;
                foreach (MatchRound r in m_Rounds)
                {
                    Score1 += r.Scores[0];
                    Score2 += r.Scores[1];
                }
                if (Score1 > Score2)
                    return Contestants[0];
                else if (Score1 < Score2)
                    return Contestants[1];
                else
                    return null;
            }
        }

        [XmlIgnore]
        public MatchStatus Status
        {
            get
            {
                if (OwnTeam != null)
                {
                    int ownScore = 0, enemyScore = 0;
                    foreach (MatchRound r in m_Rounds)
                    {
                        ownScore += r.Scores[m_Contestants.IndexOf(OwnTeam)];
                        enemyScore += r.Scores[m_Contestants.IndexOf(Enemy)];
                    }
                    if (ownScore > enemyScore)
                        return MatchStatus.Won;
                    else if (ownScore < enemyScore)
                        return MatchStatus.Lost;
                    else if (ownScore == 0 && enemyScore == 0)
                        return MatchStatus.Undefined;
                    else
                        return MatchStatus.Drawn;
                }
                else
                    return MatchStatus.Undefined;
            }
        }

        public Stream GetInputStream(MatchFile f)
        {
            return m_ZipFile.GetInputStream(m_ZipFile.GetEntry(f.Filename));
        }

        public DateTime MatchDate
        {
            get
            {
                return m_MatchDate;
            }
            set
            {
                m_MatchDate = value;
            }
        }

        [XmlIgnore]
        public MatchFile[] Replays
        {
            get
            {
                List<MatchFile> Replays = new List<MatchFile>();
                foreach (MatchFile f in m_Files)
                    if (f.FileType == MatchFileType.Replay)
                        Replays.Add(f);
                return Replays.ToArray();
            }
        }

        [XmlIgnore]
        public MatchFile[] Screenshots
        {
            get
            {
                List<MatchFile> Screenshots = new List<MatchFile>();
                foreach (MatchFile f in m_Files)
                    if (f.FileType == MatchFileType.Screenshot)
                        Screenshots.Add(f);
                return Screenshots.ToArray();
            }
        }

        [XmlIgnore]
        public MatchFile[] Aequitasfiles
        {
            get
            {
                List<MatchFile> Aequitasfiles = new List<MatchFile>();
                foreach (MatchFile f in m_Files)
                    if (f.FileType == MatchFileType.Aequitas)
                        Aequitasfiles.Add(f);
                return Aequitasfiles.ToArray();
            }
        }

        public class ParseSettings
        {
            public ParseSettings(string Aequitasname, ushort DemoVariationPercent)
            {
                this.Aequitasname = Aequitasname;
                this.DemoVariationPercent = DemoVariationPercent;
            }
            public string Aequitasname;
            public ushort DemoVariationPercent;
        };

        public void AutoRenameFiles(ConsoleEventItemCollection consoleevents, ParseSettings p)
        {
            MatchFile[] screenshots = this.Screenshots;
            MatchFile[] replays = this.Replays;

            foreach (MatchFile f in this.Aequitasfiles)
                f.TargetName = p.Aequitasname;

            #region Rename status screenshots
            foreach (ConsoleEventItem ev in consoleevents)
            {
                if (ev.EventType != ConsoleEventItemType.StatusScreenshotTaken)
                    continue;
                MatchFile closestscrmatch = null;
                long TimeDif = Int64.MaxValue;
                foreach (MatchFile f in screenshots)
                {
                    //has the screenshot already been autorenamed?
                    if (f.WasAutoRenamed)
                        continue;
                    long NewTimeDif = f.LastChanged.Subtract(ev.EventTime).Ticks;
                    if (NewTimeDif < TimeDif && NewTimeDif > 0)
                    {
                        closestscrmatch = f;
                        TimeDif = NewTimeDif;
                    }
                }
                if (closestscrmatch != null && TimeDif <= new TimeSpan(0, 0, 0, 10).Ticks)
                {
                    closestscrmatch.TargetName = "Status";
                    closestscrmatch.WasAutoRenamed = true;
                    //count++;
                }
            }
            #endregion


            #region Rename replays and roundscreenshots
            int DemoNum = 0;
            int renamedscreencount = 0;

            long averagesize = 0;
            int n = 0;
            foreach (MatchFile f in replays)
            {
                averagesize += f.Size;
                n++;
            }
            if (n > 0)
                averagesize = averagesize / n;

            foreach (MatchFile f in replays)
            {
                if (f.Size > averagesize * (1 - (double)p.DemoVariationPercent / 100))
                {
                    f.TargetName = String.Format("Round {0}", ++DemoNum);
                    f.WasAutoRenamed = true;
                    MatchFile closestmatch = null;
                    long TimeDif = Int64.MaxValue;
                    foreach (MatchFile f2 in screenshots)
                    {
                        //has the screenshot already been autorenamed?
                        if (f2.WasAutoRenamed)
                            continue;
                        long NewTimeDif = f.LastChanged.Subtract(f2.LastChanged).Ticks;
                        //if (NewTimeDif < 0)
                        //NewTimeDif = -NewTimeDif;
                        if (NewTimeDif < TimeDif && NewTimeDif >= 0)
                        {
                            closestmatch = f2;
                            TimeDif = NewTimeDif;
                        }
                    }
                    if (closestmatch != null && TimeDif < TimeSpan.TicksPerMinute)
                    {
                        closestmatch.TargetName = String.Format("Round {0}", DemoNum);
                        //closestmatch.Checked = true;
                        closestmatch.WasAutoRenamed = true;
                        renamedscreencount++;
                    }
                }
            }
            #endregion
        }

        internal void OnFileStatusChanged(MatchFile sender)
        {
            if (FileStatusChanged != null)
                FileStatusChanged(this, new FileStatusChangedEventArgs(sender));
        }
        private string m_Login;
        [XmlIgnore]
        public string LoginCookie
        {
            get { return m_Login; }
            set { m_Login = value; }
        }

        private int m_MaxConcurrentUploads = 2;
        private Queue<MatchFile> m_UploadQueue = new Queue<MatchFile>();
        public void StartUpload(MatchFile file)
        {
            lock (m_UploadQueue)
                m_UploadQueue.Enqueue(file);
            ProcessQueue();
        }

        public long UploadsRunning
        {
            get
            {
                return Interlocked.Read(ref m_RunningUploads);
            }
        }

        public void CancelUploads()
        {
            lock (m_UploadQueue)
                m_UploadQueue.Clear();
            foreach (MatchFile f in m_Files)
                f.CancelUpload();
        }

        private void ProcessQueue()
        {
            lock (m_UploadQueue)
                if (m_UploadQueue.Count > 0 && Interlocked.Read(ref m_RunningUploads) < m_MaxConcurrentUploads)
                    m_UploadQueue.Dequeue().StartUpload(m_Login);
        }

        internal void OnUploadFinished(MatchFile sender)
        {
            OnFileStatusChanged(sender);
            Interlocked.Decrement(ref m_RunningUploads);
            ProcessQueue();
            if (m_UploadQueue.Count <= 0 && Interlocked.Read(ref m_RunningUploads) <= 0)
            {
                EventHandler h = UploadComplete;
                if (h != null)
                    h(this, new EventArgs());
            }
        }

        private long m_RunningUploads;
        internal void OnUploadStarted(MatchFile sender)
        {
            Interlocked.Increment(ref m_RunningUploads);
        }

        private string m_Gamename = "hl";
        private string m_Modname = "cstrike";

        public string Gamename
        {
            get
            {
                return m_Gamename;
            }
            set
            {
                m_Gamename = value;
            }
        }

        public string ID
        {
            get
            {
                return MatchHelpers.getIDFromLink(m_Matchlink);
            }
        }

        public string Modname
        {
            get
            {
                return m_Modname;
            }
            set
            {
                m_Modname = value;
            }
        }
    }
}
